
using System;
using System.Collections;
using System.Collections.Generic;
using IrrlichtNETCP;
using IrrlichtNETCP.Inheritable;
using Ode.NET;

namespace kinematix
{
	
	public class CollideEventArgs :EventArgs
	{
		public kPhysNode Collider;
		public CollideEventArgs(kPhysNode obj)
		{
			Collider = obj;
		}
	}
	
	public delegate void EventCallback(kPhysNode sender, EventArgs args);
	public delegate void CollideCallback(kPhysNode sender, CollideEventArgs args);
	
	public enum BodyType
	{
		Box = 0,
		Sphere,
		TriangleMesh,
		Cylinder,
		Capsule
	}		

	public class kPhysNode : ISceneNode
	{
		internal bool enabled;
		internal SceneManager scene;
		internal bool selected;
		internal bool selectable;
	
		internal bool movable;
		internal bool debug;	
		public phBody phys_body;
		public SceneNode scene_node;
		public kWorld wrld;
		public VideoDriver driver;
		public Material mat;
		
		public TextSceneNode text;
		
		BodyType bodyType;
		
		Box3D bbox;
		
		public List<kForce> forces;

		public EventCallback OnClick;
		public EventCallback OnMouseOver;
		public EventCallback OnMouseLeave;
		public EventCallback OnMouseDown;
		public EventCallback OnMouseUp;
		public CollideCallback OnCollide;
		
		PhysicsCoeffs surface;
	
		public kPhysNode(kWorld world, int id, Vector3D pos, BodyType type, string mname, bool isMovable) :
																	base(world, world.SceneManager, id)
		{
			bodyType = type;
			wrld = world;
			scene = world.SceneManager;
			driver = scene.VideoDriver;
			
			AnimatedMesh mesh = scene.GetMesh(mname);
			if (mesh == null) return;

			scene_node = scene.AddAnimatedMeshSceneNode(mesh);
			scene_node.ID = id;
			//(scene_node as AnimatedMeshSceneNode).AddShadowVolumeSceneNode(-1, true, 10000f);
			scene_node.Parent = this;
			bbox = scene_node.BoundingBox;
			float lx = Math.Abs(bbox.MaxEdge.X)+Math.Abs(bbox.MinEdge.X);
			float ly = Math.Abs(bbox.MaxEdge.Y)+Math.Abs(bbox.MinEdge.Y);
			float lz = Math.Abs(bbox.MaxEdge.Z)+Math.Abs(bbox.MinEdge.Z);
			
				
			switch (bodyType)
			{
				case BodyType.Box:
//					scene.MeshManipulator.MakePlanarTextureMapping(mesh.GetMesh(0),1f);
					phys_body = new phBody (world.World, world.Space, pos, new Vector3D(lx, ly, lz), bodyType, isMovable, mesh);			
					mat = scene_node.GetMaterial(0);					
					mat.Texture1 = driver.GetTexture("uv.png");
					mat.DiffuseColor = Color.White;
					mat.AmbientColor = Color.White;
					mat.Shininess = 0.0f;					
					break;
				case BodyType.Sphere:
					//scene_node.Scale = new Vector3D(0.5f,0.5f,0.5f);
					phys_body = new phBody (world.World, world.Space, pos, new Vector3D(lx/2, ly/2, lz/2), bodyType, isMovable, mesh);					
					mat = scene_node.GetMaterial(0);					
					mat.Texture1 = driver.GetTexture("chromblu.tga");
					mat.MaterialType = MaterialType.SphereMap;
					mat.DiffuseColor = Color.White;
					mat.AmbientColor = Color.White;								
					break;
				case BodyType.Cylinder:
					phys_body = new phBody (world.World, world.Space, pos, new Vector3D(lx/2, ly, lz), bodyType, isMovable, mesh);				
					mat = scene_node.GetMaterial(0);					
					mat.Texture1 = driver.GetTexture("crate.jpg");
					mat.DiffuseColor = Color.White;
					mat.AmbientColor = Color.White;
					break;
	
				case BodyType.Capsule:
					phys_body = new phBody(world.World, world.Space, pos, new Vector3D(lx/2, ly, lz), bodyType, isMovable, mesh);
					mat = scene_node.GetMaterial(0);					
					mat.Texture1 = driver.GetTexture("chromblu.tga");
					mat.DiffuseColor = Color.White;
					mat.AmbientColor = Color.White;
					break;
				case BodyType.TriangleMesh:
					phys_body = new phBody (world.World, world.Space, pos, new Vector3D(lx, ly, lz), bodyType, isMovable, mesh);
					mat = scene_node.GetMaterial(0);					
					mat.Texture1 = driver.GetTexture("chromblu.tga");
					mat.DiffuseColor = Color.White;
					mat.AmbientColor = Color.White;					
					break;
					
			}

			phys_body.SetDisableParams(0.01f, 0.01f, 10);
			phys_body.SetUserData(this._raw);
			
			forces = new List<kinematix.kForce>();
			
			selected = false;
			selectable = false;
			movable = isMovable;

			text = scene.AddTextSceneNode(world.Device.GUIEnvironment.Skin.Font, "", Color.White, this);
		
			surface = new PhysicsCoeffs();
			surface.mu = 0.02f;
			surface.cfm = 0.002f;
			surface.erp = 0.6f;
			surface.slip1 = 0.001f;
			surface.slip2 = 0.001f;
			world.AddNode(this);
			this.Name = "__Node__"+ID;			
		
		}
		
		public float Mass
		{
			get{
				return phys_body.Mass;
			}
			set{
				phys_body.Mass = value;
			}
		}
		
		public void Destroy()
		{
			phys_body.Destroy();
		}		
		
		public BodyType Type
		{
			get
			{
			return bodyType;
			}
		}
		
		public void AddForce(kForce _force)
		{
			if (movable)
			{
				forces.Add(_force);
			}
		}
		
		
		public void ApplyForce(float dt)
		{
			if (movable&&forces.Count != 0)
			{
			
				List<kForce>.Enumerator en = forces.GetEnumerator();
				while (en.MoveNext())
				{
					en.Current.Apply(phys_body, dt);
				}
				
				for (int i =0; i<forces.Count; i++)
				{
					if (forces[i].Life <= 0.0f)
					{
						forces.RemoveAt(i);
					}
				}
			}
			
			
		}
		
		public override Vector3D Position
		{
			get{
				return scene_node.Position;
			}
		}
			
		public void Update()
		{
			phys_body.Update();
			scene_node.Position = phys_body.Position;
			scene_node.Rotation = phys_body.Rotation;
			
			text.Text = "Speed="+phys_body.Velocity;
			text.Position = scene_node.Position;

		}
		
		public void Click()
		{
		
			if (OnClick != null)
			{
				OnClick(this, null);
			}
		}

		public void Collide(kPhysNode obj)
		{
		
			if (OnCollide != null)
			{
				CollideEventArgs args = new CollideEventArgs(obj);
				OnCollide(this, args);
			}
		}
		
		public void Select()
		{
			//Console.WriteLine("I am selected! " + ID);
			selected = true;
		}
		
		public void Deselect()
		{
			//Console.WriteLine("I am deselected! " + ID);
			selected = false;
		}
		
		public bool Selected
		{
			get { return selected; }
		}
		
		public override void OnRegisterSceneNode ()
		{
			if (Visible)
			{
				Update();
/*				if (selected)
				{
					debug = true;	
				} else {
					debug = false;
				}*/
				
		
				scene.RegisterNodeForRendering(this, SceneNodeRenderPass.Solid);
				base.OnRegisterSceneNode();
			}
		}



		public override void Render()
		{
			//driver.SetMaterial(scene_node.GetMaterial(0));

/*			if (selected) 
			{
				scene_node.GetMaterial(0).Lighting = false;
				//driver.Draw3DBox (bbox, Color.White);
			} else {
				scene_node.GetMaterial(0).Lighting = true;			
			}*/
			/*if (Type == BodyType.TriangleMesh)
			{*/
			//}
				/*Vertex3D[] vects = new Vertex3D[282*3];
				
				for (int i=0; i < 282*3; i+=3)
				{
				d.Vector3 v1 = new d.Vector3();
				d.Vector3 v2 = new d.Vector3();
				d.Vector3 v3 = new d.Vector3();
				
				d.GeomTriMeshGetTriangle(phys_body.Geom, 0,  ref v1, ref v2, ref v3);
/*				Console.WriteLine(v1.X+" "+v1.Y+" "+v1.Z);
				Console.WriteLine(v2.X+" "+v2.Y+" "+v2.Z);
				Console.WriteLine(v3.X+" "+v3.Y+" "+v3.Z);*/
				
				/*vects[i].Position = new Vector3D(v1.X, v1.Y, v1.Z);
				vects[i+1].Position = new Vector3D(v2.X, v2.Y, v2.Z);
				vects[i+2].Position = new Vector3D(v3.X, v3.Y, v3.Z);
				}
				
				driver.DrawVertexPrimitiveList(vects, PrimitiveType.Triangles);*/			
			//scene_node.Render();
			base.Render();
		}
		
		public override void OnAnimate (uint timeMS)
		{
			//phys_body.PostUpdate();
			base.OnAnimate(timeMS);
		}

		
		public override uint MaterialCount
		{
			get
			{
				return 1;
			}
		}
		
		
		public override Material GetMaterial (int i)
		{
			return mat;
		}

		public bool Movable
		{
			get
				{
					return movable;
				}
			set
				{
					movable = value;
				}		
		}
		
		public bool Selectable
		{
			get {
				return selectable;
			}
			set {
				selectable = value;
			}
		}
		
		public bool Enabled
		{
			get { return enabled; }
			set { enabled = value; }
		}		
		
		public override int ID {
			get {
			return scene_node.ID; }
		}

		public phBody Body
		{
			get {
				return phys_body;
			}
		}		
		
		public override Vector3D Rotation {
			get { return scene_node.Rotation; }
			set { phys_body.Rotation = value; }
		}

		public PhysicsCoeffs Surface
		{
			get
			{
				return surface;
			}
			set{
				surface = value;
			}
		}
		
		public float Mu
		{
			get{
				return surface.mu;
			}
			set{
				surface.mu = value;
			}
		}
		

	}
}
